home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / calendar-dialog-utils.js < prev    next >
Text File  |  2008-01-23  |  35KB  |  834 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is Oracle Corporation
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  23.  *   Michael Buettner <michael.buettner@sun.com>
  24.  *   Stefan Sitter <ssitter@gmail.com>
  25.  *   Philipp Kewisch <mozilla@kewis.ch>
  26.  *
  27.  * Alternatively, the contents of this file may be used under the terms of
  28.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  29.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30.  * in which case the provisions of the GPL or the LGPL are applicable instead
  31.  * of those above. If you wish to allow use of your version of this file only
  32.  * under the terms of either the GPL or the LGPL, and not to allow others to
  33.  * use your version of this file under the terms of the MPL, indicate your
  34.  * decision by deleting the provisions above and replace them with the notice
  35.  * and other provisions required by the GPL or the LGPL. If you do not delete
  36.  * the provisions above, a recipient may use your version of this file under
  37.  * the terms of any one of the MPL, the GPL or the LGPL.
  38.  *
  39.  * ***** END LICENSE BLOCK ***** */
  40.  
  41. /* use with textfields oninput to only allow integers */
  42. function validateIntegerRange(event, lowerBound, upperBound) {
  43.     validateIntegers(event);
  44.  
  45.     var num = Number(event.target.value);
  46.  
  47.     // Only modify the number if a value is entered, otherwise deleting the
  48.     // value (to maybe enter a new number) will cause the field to be set to the
  49.     // lower bound.
  50.     if (event.target.value != "" && (num < lowerBound || num > upperBound)) {
  51.         event.target.value = Math.min(Math.max(num, lowerBound), upperBound);
  52.         event.preventDefault();
  53.     }
  54. }
  55.  
  56. function validateIntegers(event) {
  57.     if (isNaN(Number(event.target.value))) {
  58.         var newValue = parseInt(event.target.value);
  59.         event.target.value = isNaN(newValue) ? "" : newValue;
  60.         event.preventDefault();
  61.     }
  62. }
  63.  
  64. function validateNaturalNums(event) {
  65.     validateIntegers(event);
  66.     var num = event.target.value;
  67.     if (num < 0) {
  68.         event.target.value = -1 * num;
  69.         event.preventDefault();
  70.     }
  71. }
  72.  
  73. /**
  74.  * This function takes the recurrence info passed as argument and creates a
  75.  * literal string representing the repeat pattern in natural language.
  76.  */
  77. function recurrenceRule2String(recurrenceInfo, startDate, endDate, allDay) {
  78.  
  79.     // Retrieve a valid recurrence rule from the currently
  80.     // set recurrence info. Bail out if there's more
  81.     // than a single rule or something other than a rule.
  82.     recurrenceInfo = recurrenceInfo.clone();
  83.     var rrules = splitRecurrenceRules(recurrenceInfo);
  84.     if (rrules[0].length == 1) {
  85.         var rule = rrules[0][0];
  86.         // currently we don't allow for any BYxxx-rules.
  87.         if (rule instanceof Components.interfaces.calIRecurrenceRule &&
  88.             !checkRecurrenceRule(rule, ['BYSECOND',
  89.                                         'BYMINUTE',
  90.                                         //'BYDAY',
  91.                                         'BYHOUR',
  92.                                         //'BYMONTHDAY',
  93.                                         'BYYEARDAY',
  94.                                         'BYWEEKNO',
  95.                                         //'BYMONTH',
  96.                                         'BYSETPOS'])) {
  97.             function day_of_week(day) {
  98.                 return Math.abs(day) % 8;
  99.             }
  100.             function day_position(day) {
  101.                 var dow = day_of_week(day);
  102.                 return (Math.abs(day) - dow) / 8 * (day < 0 ? -1 : 1);
  103.             }
  104.  
  105.             var ruleString = "???";
  106.             if (rule.type == 'DAILY') {
  107.                 if (checkRecurrenceRule(rule, ['BYDAY'])) {
  108.                     var days = rule.getComponent("BYDAY", {});
  109.                     var weekdays = [2, 3, 4, 5, 6];
  110.                     if (weekdays.length == days.length) {
  111.                         for (var i = 0; i < weekdays.length; i++) {
  112.                             if (weekdays[i] != days[i]) {
  113.                                 break;
  114.                             }
  115.                         }
  116.                         if (i == weekdays.length) {
  117.                             ruleString = calGetString(
  118.                                 "sun-calendar-event-dialog",
  119.                                 "repeatDetailsRuleDaily4");
  120.                         }
  121.                     }
  122.                 } else {
  123.                     if (rule.interval == 1) {
  124.                         ruleString = calGetString(
  125.                             "sun-calendar-event-dialog",
  126.                             "repeatDetailsRuleDaily1");
  127.                     } else if (rule.interval == 2) {
  128.                         ruleString = calGetString(
  129.                             "sun-calendar-event-dialog",
  130.                             "repeatDetailsRuleDaily2");
  131.                     } else {
  132.                         ruleString = calGetString(
  133.                           "sun-calendar-event-dialog",
  134.                           "repeatDetailsRuleDaily3",
  135.                           [ rule.interval ], 1);
  136.                     }
  137.                 }
  138.             } else if (rule.type == 'WEEKLY') {
  139.                 // weekly recurrence, currently we
  140.                 // support a single 'BYDAY'-rule only.
  141.                 if (checkRecurrenceRule(rule, ['BYDAY'])) {
  142.                     // create a string like 'Monday, Tuesday and
  143.                     // Wednesday'
  144.                     var days = rule.getComponent("BYDAY", {});
  145.                     var weekdays = "";
  146.                     for (var i = 0; i < days.length; i++) {
  147.                         weekdays += calGetString(
  148.                             "sun-calendar-event-dialog",
  149.                             "repeatDetailsDay" + days[i]);
  150.                         if (days.length > 1 && i == (days.length - 2)) {
  151.                             weekdays += ' ' + calGetString(
  152.                                 "sun-calendar-event-dialog",
  153.                                 "repeatDetailsAnd") + ' ';
  154.                         } else if (i < days.length - 1) {
  155.                             weekdays += ', ';
  156.                         }
  157.                     }
  158.  
  159.                     // now decorate this with 'every other week, etc'.
  160.                     if (rule.interval == 1) {
  161.                         ruleString = calGetString(
  162.                           "sun-calendar-event-dialog",
  163.                           "repeatDetailsRuleWeekly1", [ weekdays ], 1);
  164.                     } else if (rule.interval == 2) {
  165.                         ruleString = calGetString(
  166.                           "sun-calendar-event-dialog",
  167.                           "repeatDetailsRuleWeekly2", [ weekdays ], 1);
  168.                     } else {
  169.                         ruleString = calGetString(
  170.                           "sun-calendar-event-dialog",
  171.                           "repeatDetailsRuleWeekly3",
  172.                           [ rule.interval, weekdays ],
  173.                           2);
  174.                     }
  175.                 } else {
  176.                     if (rule.interval == 1) {
  177.                         ruleString = calGetString(
  178.                             "sun-calendar-event-dialog",
  179.                             "repeatDetailsRuleWeekly4");
  180.                     } else if (rule.interval == 2) {
  181.                         ruleString = calGetString(
  182.                             "sun-calendar-event-dialog",
  183.                             "repeatDetailsRuleWeekly5");
  184.                     } else {
  185.                         ruleString = calGetString(
  186.                           "sun-calendar-event-dialog",
  187.                           "repeatDetailsRuleWeekly6",
  188.                           [ rule.interval ], 1);
  189.                     }
  190.                 }
  191.             } else if (rule.type == 'MONTHLY') {
  192.                 if (checkRecurrenceRule(rule, ['BYDAY'])) {
  193.                     var component = rule.getComponent("BYDAY", {});
  194.                     var byday = component[0];
  195.                     var ordinal_string =
  196.                         calGetString(
  197.                             "sun-calendar-event-dialog",
  198.                             "repeatDetailsOrdinal" + day_position(byday));
  199.                     var day_string =
  200.                         calGetString(
  201.                             "sun-calendar-event-dialog",
  202.                             "repeatDetailsDay" + day_of_week(byday));
  203.  
  204.                     if (rule.interval == 1) {
  205.                         ruleString = calGetString(
  206.                           "sun-calendar-event-dialog",
  207.                           "repeatDetailsRuleMonthly1",
  208.                           [ ordinal_string, day_string ],
  209.                           2);
  210.                     } else if (rule.interval == 2) {
  211.                         ruleString = calGetString(
  212.                           "sun-calendar-event-dialog",
  213.                           "repeatDetailsRuleMonthly2",
  214.                           [ ordinal_string, day_string ],
  215.                           2);
  216.                     } else {
  217.                         ruleString = calGetString(
  218.                           "sun-calendar-event-dialog",
  219.                           "repeatDetailsRuleMonthly3",
  220.                           [ ordinal_string, day_string, rule.interval ],
  221.                           3);
  222.                     }
  223.                 } else if (checkRecurrenceRule(rule, ['BYMONTHDAY'])) {
  224.                     var component = rule.getComponent("BYMONTHDAY", {});
  225.  
  226.                     var day_string = "";
  227.                     for (var i = 0; i < component.length; i++) {
  228.                         // TODO: we also need to handle BYMONTHDAY rules with
  229.                         // negative array elements, but we're currently in string
  230.                         // freeze for 0.7 so I can't add the necessary bits and
  231.                         // pieces.
  232.                         if (component[i] < 0) {
  233.                             return null;
  234.                         }
  235.                         day_string += component[i];
  236.                         if (component.length > 1 &&
  237.                             i == (component.length - 2)) {
  238.                             day_string += ' ' +calGetString(
  239.                                 "sun-calendar-event-dialog",
  240.                                 "repeatDetailsAnd") + ' ';
  241.                         } else if (i < component.length-1) {
  242.                             day_string += ', ';
  243.                         }
  244.                     }
  245.  
  246.                     if (rule.interval == 1) {
  247.                         ruleString = calGetString(
  248.                           "sun-calendar-event-dialog",
  249.                           "repeatDetailsRuleMonthly4",
  250.                           [ day_string ], 1);
  251.                     } else if (rule.interval == 2) {
  252.                         ruleString = calGetString(
  253.                           "sun-calendar-event-dialog",
  254.                           "repeatDetailsRuleMonthly5",
  255.                           [ day_string ], 1);
  256.                     } else {
  257.                         ruleString = calGetString(
  258.                           "sun-calendar-event-dialog",
  259.                           "repeatDetailsRuleMonthly6",
  260.                           [ day_string, rule.interval ],
  261.                           2);
  262.                     }
  263.                 } else {
  264.                     if (rule.interval == 1) {
  265.                         ruleString = calGetString(
  266.                           "sun-calendar-event-dialog",
  267.                           "repeatDetailsRuleMonthly4",
  268.                           [ startDate.day ], 1);
  269.                     } else if (rule.interval == 2) {
  270.                         ruleString = calGetString(
  271.                           "sun-calendar-event-dialog",
  272.                           "repeatDetailsRuleMonthly5",
  273.                           [ startDate.day ], 1);
  274.                     } else {
  275.                         ruleString = calGetString(
  276.                           "sun-calendar-event-dialog",
  277.                           "repeatDetailsRuleMonthly6",
  278.                           [ startDate.day, rule.interval ],
  279.                           2);
  280.                     }
  281.                 }
  282.             } else if (rule.type == 'YEARLY') {
  283.                 if (checkRecurrenceRule(rule, ['BYMONTH']) &&
  284.                     checkRecurrenceRule(rule, ['BYMONTHDAY'])) {
  285.                     bymonth = rule.getComponent("BYMONTH", {});
  286.                     bymonthday = rule.getComponent("BYMONTHDAY", {});
  287.  
  288.                     if (bymonth.length == 1 && bymonthday.length == 1) {
  289.                         var month_string =
  290.                             calGetString(
  291.                                 "sun-calendar-event-dialog",
  292.                                 "repeatDetailsMonth" + bymonth[0]);
  293.  
  294.                         if (rule.interval == 1) {
  295.                             ruleString = calGetString(
  296.                               "sun-calendar-event-dialog",
  297.                               "repeatDetailsRuleYearly1",
  298.                               [ month_string, bymonthday[0] ],
  299.                               2);
  300.                         } else if (rule.interval == 2) {
  301.                             ruleString = calGetString(
  302.                               "sun-calendar-event-dialog",
  303.                               "repeatDetailsRuleYearly2",
  304.                               [ month_string, bymonthday[0] ],
  305.                               2);
  306.                         } else {
  307.                             ruleString = calGetString(
  308.                               "sun-calendar-event-dialog",
  309.                               "repeatDetailsRuleYearly3",
  310.                               [ month_string,
  311.                                 bymonthday[0],
  312.                                 rule.interval ],
  313.                               3);
  314.                         }
  315.                     }
  316.                 } else if (checkRecurrenceRule(rule, ['BYMONTH']) &&
  317.                            checkRecurrenceRule(rule, ['BYDAY'])) {
  318.                     bymonth = rule.getComponent("BYMONTH", {});
  319.                     byday = rule.getComponent("BYDAY", {});
  320.  
  321.                     if (bymonth.length == 1 && byday.length == 1) {
  322.                         var month_string =
  323.                             calGetString(
  324.                                 "sun-calendar-event-dialog",
  325.                                 "repeatDetailsMonth" + bymonth[0]);
  326.                         var ordinal_string =
  327.                             calGetString(
  328.                                 "sun-calendar-event-dialog",
  329.                                 "repeatDetailsOrdinal" +
  330.                                     day_position(byday[0]));
  331.                         var day_string =
  332.                             calGetString(
  333.                                 "sun-calendar-event-dialog",
  334.                                 "repeatDetailsDay" + day_of_week(byday[0]));
  335.  
  336.                         if (rule.interval == 1) {
  337.                             ruleString = calGetString(
  338.                               "sun-calendar-event-dialog",
  339.                               "repeatDetailsRuleYearly4",
  340.                               [ ordinal_string, day_string, month_string ],
  341.                               3);
  342.                         } else if (rule.interval == 2) {
  343.                             ruleString = calGetString(
  344.                               "sun-calendar-event-dialog",
  345.                               "repeatDetailsRuleYearly5",
  346.                               [ ordinal_string, day_string, month_string ],
  347.                               3);
  348.                         } else {
  349.                             ruleString = calGetString(
  350.                               "sun-calendar-event-dialog",
  351.                               "repeatDetailsRuleYearly6",
  352.                               [ ordinal_string,
  353.                                 day_string,
  354.                                 month_string,
  355.                                 rule.interval ],
  356.                               4);
  357.                         }
  358.                     }
  359.                 } else {
  360.                     var month_string =
  361.                         calGetString(
  362.                             "sun-calendar-event-dialog",
  363.                             "repeatDetailsMonth" + (startDate.month+1));
  364.                     if (rule.interval == 1) {
  365.                         ruleString = calGetString(
  366.                           "sun-calendar-event-dialog",
  367.                           "repeatDetailsRuleYearly1",
  368.                           [ month_string, startDate.day ],
  369.                           2);
  370.                     } else if (rule.interval == 2) {
  371.                         ruleString = calGetString(
  372.                           "sun-calendar-event-dialog",
  373.                           "repeatDetailsRuleYearly2",
  374.                           [ month_string, startDate.day ],
  375.                           2);
  376.                     } else {
  377.                         ruleString = calGetString(
  378.                           "sun-calendar-event-dialog",
  379.                           "repeatDetailsRuleYearly3",
  380.                           [ month_string,
  381.                             startDate.day,
  382.                             rule.interval ],
  383.                           3);
  384.                     }
  385.                 }
  386.             }
  387.  
  388.             var kDefaultTimezone = calendarDefaultTimezone();
  389.  
  390.             var dateFormatter =
  391.                 Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
  392.                 .getService(Components.interfaces.calIDateTimeFormatter);
  393.  
  394.             var detailsString;
  395.             if (!endDate || allDay) {
  396.                 if (rule.isFinite) {
  397.                     if (rule.isByCount) {
  398.                         detailsString = calGetString(
  399.                             "sun-calendar-event-dialog",
  400.                             "repeatDetailsCountAllDay",
  401.                             [ ruleString,
  402.                               dateFormatter.formatDateShort(startDate),
  403.                               rule.count ], 3);
  404.                     } else {
  405.                         var untilDate = rule.endDate.getInTimezone(kDefaultTimezone);
  406.                         detailsString = calGetString(
  407.                             "sun-calendar-event-dialog",
  408.                             "repeatDetailsUntilAllDay",
  409.                             [ ruleString,
  410.                               dateFormatter.formatDateShort(startDate),
  411.                               dateFormatter.formatDateShort(untilDate) ],
  412.                             3);
  413.                     }
  414.                   } else {
  415.                       detailsString = calGetString(
  416.                           "sun-calendar-event-dialog",
  417.                           "repeatDetailsInfiniteAllDay",
  418.                           [ ruleString,
  419.                             dateFormatter.formatDateShort(startDate) ], 2);
  420.                   }
  421.               } else {
  422.                 if (rule.isFinite) {
  423.                     if (rule.isByCount) {
  424.                         detailsString = calGetString(
  425.                             "sun-calendar-event-dialog",
  426.                             "repeatDetailsCount",
  427.                             [ ruleString,
  428.                               dateFormatter.formatDateShort(startDate),
  429.                               rule.count,
  430.                               dateFormatter.formatTime(startDate),
  431.                               dateFormatter.formatTime(endDate) ], 5);
  432.                     } else {
  433.                         var untilDate = rule.endDate.getInTimezone(kDefaultTimezone);
  434.                         detailsString = calGetString(
  435.                             "sun-calendar-event-dialog",
  436.                             "repeatDetailsUntil",
  437.                             [ ruleString,
  438.                               dateFormatter.formatDateShort(startDate),
  439.                               dateFormatter.formatDateShort(untilDate),
  440.                               dateFormatter.formatTime(startDate),
  441.                               dateFormatter.formatTime(endDate) ], 5);
  442.                     }
  443.                 } else {
  444.                     detailsString = calGetString(
  445.                         "sun-calendar-event-dialog",
  446.                         "repeatDetailsInfinite",
  447.                         [ ruleString,
  448.                           dateFormatter.formatDateShort(startDate),
  449.                           dateFormatter.formatTime(startDate),
  450.                           dateFormatter.formatTime(endDate) ], 4);
  451.                 }
  452.             }
  453.             return detailsString;
  454.         }
  455.     }
  456.     return null;
  457. }
  458.  
  459. function splitRecurrenceRules(recurrenceInfo) {
  460.     var ritems = recurrenceInfo.getRecurrenceItems({});
  461.     var rules = [];
  462.     var exceptions = [];
  463.     for each (var r in ritems) {
  464.         if (r.isNegative) {
  465.             exceptions.push(r);
  466.         } else {
  467.             rules.push(r);
  468.         }
  469.     }
  470.     return [rules, exceptions];
  471. }
  472.  
  473. function checkRecurrenceRule(aRule, aArray) {
  474.     for each (var comp in aArray) {
  475.         var ruleComp = aRule.getComponent(comp, {});
  476.         if (ruleComp && ruleComp.length > 0) {
  477.             return true;
  478.         }
  479.     }
  480.     return false;
  481. }
  482.  
  483. function dispose() {
  484.     var args = window.arguments[0];
  485.     if (args.job && args.job.dispose) {
  486.         args.job.dispose();
  487.     }
  488. }
  489.  
  490. function editReminder() {
  491.     var customReminder =
  492.         document.getElementById("reminder-custom-menuitem");
  493.     var args = new Object();
  494.     args.reminder = customReminder.reminder;
  495.     var savedWindow = window;
  496.     args.onOk = function(reminder) {
  497.         customReminder.reminder = reminder;
  498.     };
  499.  
  500.     window.setCursor("wait");
  501.  
  502.     // open the dialog modally
  503.     openDialog(
  504.         "chrome://calendar/content/sun-calendar-event-dialog-reminder.xul",
  505.         "_blank",
  506.         "chrome,titlebar,modal,resizable",
  507.         args);
  508. }
  509.  
  510. function updateReminderDetails() {
  511.     // find relevant elements in the document
  512.     var reminderPopup = document.getElementById("item-alarm");
  513.     var reminderDetails = document.getElementById("reminder-details");
  514.     var reminder = document.getElementById("reminder-custom-menuitem").reminder;
  515.  
  516.     // first of all collapse the details text. if we fail to
  517.     // create a details string, we simply don't show anything.
  518.     reminderDetails.setAttribute("collapsed", "true");
  519.  
  520.     // don't try to show the details text
  521.     // for anything but a custom recurrence rule.
  522.     if (reminderPopup.value == "custom" && reminder) {
  523.         var unitString;
  524.         switch (reminder.unit) {
  525.             case 'minutes':
  526.                 unitString = Number(reminder.length) <= 1 ?
  527.                     calGetString(
  528.                         "sun-calendar-event-dialog",
  529.                         "reminderCustomUnitMinute") :
  530.                     calGetString(
  531.                         "sun-calendar-event-dialog",
  532.                         "reminderCustomUnitMinutes");
  533.                 break;
  534.             case 'hours':
  535.                 unitString = Number(reminder.length) <= 1 ?
  536.                     calGetString(
  537.                         "sun-calendar-event-dialog",
  538.                         "reminderCustomUnitHour") :
  539.                     calGetString(
  540.                         "sun-calendar-event-dialog",
  541.                         "reminderCustomUnitHours");
  542.                 break;
  543.             case 'days':
  544.                 unitString = Number(reminder.length) <= 1 ?
  545.                     calGetString(
  546.                         "sun-calendar-event-dialog",
  547.                         "reminderCustomUnitDay") :
  548.                     calGetString(
  549.                         "sun-calendar-event-dialog",
  550.                         "reminderCustomUnitDays");
  551.                 break;
  552.         }
  553.  
  554.         var relationString;
  555.         switch (reminder.relation) {
  556.             case 'START':
  557.                 relationString = calGetString(
  558.                     "sun-calendar-event-dialog",
  559.                     "reminderCustomRelationStart");
  560.                 break;
  561.             case 'END':
  562.                 relationString = calGetString(
  563.                     "sun-calendar-event-dialog",
  564.                     "reminderCustomRelationEnd");
  565.                 break;
  566.         }
  567.  
  568.         var originString;
  569.         if (reminder.origin && reminder.origin < 0) {
  570.             originString = calGetString(
  571.                 "sun-calendar-event-dialog",
  572.                 "reminderCustomOriginEnd");
  573.         } else {
  574.             originString = calGetString(
  575.                 "sun-calendar-event-dialog",
  576.                 "reminderCustomOriginBegin");
  577.         }
  578.  
  579.         var detailsString = calGetString(
  580.           "sun-calendar-event-dialog",
  581.           "reminderCustomTitle",
  582.           [ reminder.length,
  583.             unitString,
  584.             relationString,
  585.             originString], 4);
  586.  
  587.         var lines = detailsString.split("\n");
  588.         reminderDetails.removeAttribute("collapsed");
  589.         while (reminderDetails.childNodes.length > lines.length) {
  590.             reminderDetails.removeChild(reminderDetails.lastChild);
  591.         }
  592.         var numChilds = reminderDetails.childNodes.length;
  593.         for (var i = 0; i < lines.length; i++) {
  594.             if (i >= numChilds) {
  595.                 var newNode = reminderDetails.childNodes[0].cloneNode(true);
  596.                 reminderDetails.appendChild(newNode);
  597.             }
  598.             var node = reminderDetails.childNodes[i];
  599.             node.setAttribute('value', lines[i]);
  600.         }
  601.     }
  602. }
  603.  
  604. var gLastAlarmSelection = 0;
  605.  
  606. function loadReminder(item) {
  607.     // select 'no reminder' by default
  608.     var reminderPopup = document.getElementById("item-alarm");
  609.     reminderPopup.selectedIndex = 0;
  610.     gLastAlarmSelection = 0;
  611.     if (!item.alarmOffset) {
  612.         return;
  613.     }
  614.  
  615.     // try to match the reminder setting with the available popup items
  616.     var origin = "1";
  617.     if (item.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_END) {
  618.         origin = "-1";
  619.     }
  620.     var duration = item.alarmOffset.clone();
  621.     var relation = "END";
  622.     if (duration.isNegative) {
  623.         duration.isNegative = false;
  624.         duration.normalize();
  625.         relation = "START";
  626.     }
  627.     var matchingItem = null;
  628.     var menuItems = reminderPopup.getElementsByTagName("menuitem");
  629.     var numItems = menuItems.length;
  630.     for (var i=0; i<numItems; i++) {
  631.         var menuitem = menuItems[i];
  632.         if (menuitem.hasAttribute("length")) {
  633.             if (menuitem.getAttribute("origin") == origin &&
  634.                 menuitem.getAttribute("relation") == relation) {
  635.                 var unit = menuitem.getAttribute("unit");
  636.                 var length = menuitem.getAttribute("length");
  637.                 if (unit == "days") {
  638.                     length = length * 60 * 60 * 24;
  639.                 } else if (unit == "hours") {
  640.                     length = length * 60 * 60;
  641.                 } else if (unit == "minutes") {
  642.                     length = length * 60;
  643.                 } else {
  644.                     continue;
  645.                 }
  646.                 if (duration.inSeconds == length) {
  647.                     matchingItem = menuitem;
  648.                     break;
  649.                 }
  650.             }
  651.         }
  652.     }
  653.  
  654.     if (matchingItem) {
  655.         var numChilds = reminderPopup.childNodes[0].childNodes.length;
  656.         for (var i = 0; i < numChilds; i++) {
  657.             var node = reminderPopup.childNodes[0].childNodes[i];
  658.             if (node == matchingItem) {
  659.                 reminderPopup.selectedIndex = i;
  660.                 break;
  661.             }
  662.         }
  663.     } else {
  664.         reminderPopup.value = 'custom';
  665.         var customReminder =
  666.             document.getElementById("reminder-custom-menuitem");
  667.         var reminder = {};
  668.         if (item.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_START) {
  669.             reminder.origin = "1";
  670.         } else {
  671.             reminder.origin = "-1";
  672.         }
  673.         var offset = item.alarmOffset.clone();
  674.         var relation = "END";
  675.         if (offset.isNegative) {
  676.             offset.isNegative = false;
  677.             offset.normalize();
  678.             relation = "START";
  679.         }
  680.         reminder.relation = relation;
  681.         if (offset.minutes) {
  682.             var minutes = offset.minutes +
  683.                           offset.hours * 60 +
  684.                           offset.days * 24 * 60 +
  685.                           offset.weeks * 60 * 24 * 7;
  686.             reminder.unit = 'minutes';
  687.             reminder.length = minutes;
  688.         } else if (offset.hours) {
  689.             var hours = offset.hours + offset.days * 24 + offset.weeks * 24 * 7;
  690.             reminder.unit = 'hours';
  691.             reminder.length = hours;
  692.         } else {
  693.             var days = offset.days + offset.weeks * 7;
  694.             reminder.unit = 'days';
  695.             reminder.length = days;
  696.         }
  697.         customReminder.reminder = reminder;
  698.     }
  699.  
  700.     // remember the selected index
  701.     gLastAlarmSelection = reminderPopup.selectedIndex;
  702. }
  703.  
  704. function saveReminder(item) {
  705.     var reminderPopup = document.getElementById("item-alarm");
  706.     if (reminderPopup.value == 'none') {
  707.         item.alarmOffset = null;
  708.         item.alarmLastAck = null;
  709.         item.alarmRelated = null;
  710.     } else {
  711.         var menuitem = reminderPopup.selectedItem;
  712.  
  713.         // custom reminder entries carry their own reminder object
  714.         // with them, pre-defined entries specify the necessary information
  715.         // as attributes attached to the menuitem elements.
  716.         var reminder = menuitem.reminder;
  717.         if (!reminder) {
  718.             reminder = {};
  719.             reminder.length = menuitem.getAttribute('length');
  720.             reminder.unit = menuitem.getAttribute('unit');
  721.             reminder.relation = menuitem.getAttribute('relation');
  722.             reminder.origin = menuitem.getAttribute('origin');
  723.         }
  724.  
  725.         var duration = Components.classes["@mozilla.org/calendar/duration;1"]
  726.                        .createInstance(Components.interfaces.calIDuration);
  727.  
  728.         duration[reminder.unit] = Number(reminder.length);
  729.         if (reminder.relation != "END") {
  730.             duration.isNegative = true;
  731.         }
  732.         duration.normalize();
  733.         item.alarmOffset = duration;
  734.  
  735.         if (Number(reminder.origin) >= 0) {
  736.             item.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
  737.         } else {
  738.             item.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_END;
  739.         }
  740.     }
  741. }
  742.  
  743. function commonUpdateReminder() {
  744.     // if a custom reminder has been selected, we show the appropriate
  745.     // dialog in order to allow the user to specify the details.
  746.     // the result will be placed in the 'reminder-custom-menuitem' tag.
  747.     var reminderPopup = document.getElementById("item-alarm");
  748.     if (reminderPopup.value == 'custom') {
  749.         // show the dialog.
  750.         // don't pop up the dialog if this happens during
  751.         // initialization of the dialog.
  752.         if (reminderPopup.hasAttribute("last-value")) {
  753.             editReminder();
  754.         }
  755.  
  756.         // Now check if the resulting custom reminder is valid.
  757.         // possibly we receive an invalid reminder if the user cancels the
  758.         // dialog. In that case we revert to the previous selection of the
  759.         // reminder drop down.
  760.         if (!document.getElementById("reminder-custom-menuitem").reminder) {
  761.             reminderPopup.selectedIndex = gLastAlarmSelection;
  762.         }
  763.     }
  764.  
  765.     // remember the current reminder drop down selection index.
  766.     gLastAlarmSelection = reminderPopup.selectedIndex;
  767.     reminderPopup.setAttribute("last-value", reminderPopup.value);
  768.  
  769.     // possibly the selected reminder conflicts with the item.
  770.     // for example an end-relation combined with a task without duedate
  771.     // is an invalid state we need to take care of. we take the same
  772.     // approach as with recurring tasks. in case the reminder is related
  773.     // to the entry date we check the entry date automatically and disable
  774.     // the checkbox. the same goes for end related reminder and the due date.
  775.     if (isToDo(window.calendarItem)) {
  776.  
  777.         // custom reminder entries carry their own reminder object
  778.         // with them, pre-defined entries specify the necessary information
  779.         // as attributes attached to the menuitem elements.
  780.         var menuitem = reminderPopup.selectedItem;
  781.         if (menuitem.value == 'none') {
  782.             enableElementWithLock("todo-has-entrydate", "reminder-lock");
  783.             enableElementWithLock("todo-has-duedate", "reminder-lock");
  784.         } else {
  785.             var reminder = menuitem.reminder;
  786.             if (!reminder) {
  787.                 reminder = {};
  788.                 reminder.length = menuitem.getAttribute('length');
  789.                 reminder.unit = menuitem.getAttribute('unit');
  790.                 reminder.relation = menuitem.getAttribute('relation');
  791.                 reminder.origin = menuitem.getAttribute('origin');
  792.             }
  793.  
  794.             // if this reminder is related to the entry date...
  795.             if (Number(reminder.origin) > 0) {
  796.  
  797.                 // ...automatically check 'has entrydate'.
  798.                 if (!getElementValue("todo-has-entrydate", "checked")) {
  799.                     setElementValue("todo-has-entrydate", "true", "checked");
  800.  
  801.                     // make sure gStartTime is properly initialized
  802.                     updateEntryDate();
  803.                 }
  804.  
  805.                 // disable the checkbox to indicate that we need
  806.                 // the entry-date. the 'disabled' state will be
  807.                 // revoked if the user turns off the repeat pattern.
  808.                 disableElementWithLock("todo-has-entrydate", "reminder-lock");
  809.                 enableElementWithLock("todo-has-duedate", "reminder-lock");
  810.             }
  811.  
  812.             // if this reminder is related to the due date...
  813.             if (Number(reminder.origin) < 0) {
  814.  
  815.                 // ...automatically check 'has duedate'.
  816.                 if (!getElementValue("todo-has-duedate", "checked")) {
  817.                     setElementValue("todo-has-duedate", "true", "checked");
  818.  
  819.                     // make sure gStartTime is properly initialized
  820.                     updateDueDate();
  821.                 }
  822.  
  823.                 // disable the checkbox to indicate that we need
  824.                 // the entry-date. the 'disabled' state will be
  825.                 // revoked if the user turns off the repeat pattern.
  826.                 disableElementWithLock("todo-has-duedate", "reminder-lock");
  827.                 enableElementWithLock("todo-has-entrydate", "reminder-lock");
  828.             }
  829.         }
  830.     }
  831.  
  832.     updateReminderDetails();
  833. }
  834.